const request = require('superagent');
const later = require('later');
const mongo = require('../app/lib/mongo');
const table = require('../app/lib/mysql');

class SESAME{
    constructor(db, ip){
        this.db = db;
        this.whiteIP = ip;
        this.counting = false;
        this.conn = null;
    }

    static __self__randomString (len = 16){
        let chars = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'];
        let res = "";
        for (let i = 0; i < len; i++) {
            let id = Math.ceil(Math.random() * 35);
            res += chars[id];
        }
        return res;
    }
    static __self__generateCookiesString(array){
        let arr = [];
        for(let co of array){
            let cookie = co.split(";");
            if(cookie[0].indexOf("delete")<0)arr.push(cookie[0])
        }
        return arr.join("; ")
    }
    static __self__ip_2_decm (ip){
        let ar = ip.split(".");
        return parseInt(ar[0])*256*256*256+parseInt(ar[1])*256*256+parseInt(ar[2])*256+parseInt(ar[3]);
    }
    static __self__sleep (time){
        return new Promise( (resolve, reject) =>{
            setTimeout(function(){resolve(null)}, time * 1000)
        })
    }
    /**
     * 获取代理IP
     * @param host
     * @param url
     * @param cacheUrl
     * @returns {Promise<*>}
     */
    async getProxyIP(host, url, cacheUrl){
        try{
            if(!this.conn) this.conn = await getSesameConnect();
            let proxyIpCache = new mongo(this.db, 'backend.proxy.ip.cache');
            let timestamp = new Date(new Date().getTime() + 10 * 1000);
            // console.log('cacheUrl', cacheUrl);
            let tempProxy = await proxyIpCache.findOne({url:cacheUrl, expire_time:{$gt:timestamp}}), proxyInfo;
            if(!tempProxy){
                proxyInfo = await this.getIP(host);
                delete proxyInfo._id;
                await proxyIpCache.update({url:cacheUrl}, {$set:proxyInfo});
            } else {
                proxyInfo = tempProxy;
            }
            const ipPond = new table(this.conn, "wt_ippond");
            let ipInfo = await ipPond.where({mark:proxyInfo.ip}).find().one();
            if(!ipInfo){
                await this.removeIP(proxyInfo.ip, proxyInfo.port);
                return await this.getProxyIP(host, url, cacheUrl);
            } else {
                return proxyInfo;
            }
        }catch (e){
            console.error(e)
        }
    }

    /**
     * 获取新的代理IP
     * @returns {Promise<void>}
     */
    async getIP(host, tryTimes = 0){
        try{
            let proxyIp = new mongo(this.db, 'backend.proxy.ip'), proxyInfo;
            let query={$or:[], expire_time:{$gt:new Date(new Date().getTime() + 10 * 1000)}, useTime:{$lt:new Date(new Date().getTime() - 30 * 1000)}}, update={}, setData = {};
            query.$or.push({[`web.${host}`]:{$lte:10}}, {web:null});
            update[`web.${host}`] = 1;
            setData.useTime = new Date();
            proxyInfo = await proxyIp.findOneAndUpdate(query, {$inc:update, $set:setData}, {upsert: false});
            if(!proxyInfo.value){
                await SESAME.__self__sleep(3);
                tryTimes++;
                if(tryTimes > 10) throw new Error("尝试次数超过十次");
                return await this.getIP(host, tryTimes);
            }
            return proxyInfo.value;
        }catch(e){
            throw e;
        }
    }

    /**
     * 获取代理IP定时器
     * @param count
     * @returns {Promise<void>}
     */
    async timing(count = false){
        if(this.counting) return;
        this.counting = true;
        try{
            if(!this.conn) this.conn = await getSesameConnect();
            let self = this;
            let account = await this.getSesameAccount();
            await this.getSesameIPList(account);
            if(count) later.setInterval(async function (){await self.timing()}, later.parse.recur().every(15).second());
        }catch (e) {
            throw e;
        }finally {
            this.counting = false;
        }
    }

    /**
     * 重置今日免费额度
     * @returns {Promise<*>}
     */
    async getSesameAccount(){
        try{
            if(!this.conn) this.conn = await getSesameConnect();
            const sesameAccount = new mongo(this.db, 'backend.zhima.account');
            // let accountNum = await sesameAccount.count({});
            // let account = await sesameAccount.findOne({}, {skip:Math.ceil(Math.random() * accountNum) - 1});
            let account = await sesameAccount.findOne({});
            const wt_users = new table(this.conn, "wt_users");
            const wt_user_package = new table(this.conn, "wt_user_package");
            let userInfo = await wt_users.where({phone:account.tel}).find().one();
            await wt_users.where({phone:account.tel}).update({
                last_login_time:parseInt(new Date().getTime() / 1000)
            }).run();

            //如果不存在account.user_id，则获取用户信息
            if(!account.user_id) {
                await sesameAccount.update({tel:account.tel}, {$set:{user_id:userInfo.id}});
                account.user_id = userInfo.id;
            }

            //获取今日免费额度
            await wt_user_package.where({uid:userInfo.id, package_type:1}).update({
                start_time:parseInt(new Date(new Date().setHours(0, 0, 0, 0) - 3 * 24 * 60 * 60 * 1000) / 1000)
                , expire_time:parseInt(new Date(new Date().setHours(24, 0, 0, 0) + 3 * 24 * 60 * 60 * 1000) / 1000)
            }).run();

            //重置账户余额
            // await wt_users.where({id:userInfo.id}).update({
            //     all_money:50.00
            //     , all_used_money:0.00
            //     , balance:50.00
            // }).run();

            //获取今日免费额度的pack id
            if(!account.pack_id){
                let packInfo = await wt_user_package.where({uid:userInfo.id, package_type:7}).find().one();
                if(!packInfo){
                    packInfo = await this.loginZhimaAccount(account);
                }
                console.log(userInfo.id, packInfo);
                await sesameAccount.update({tel:account.tel}, {$set:{pack_id :packInfo.id}});
                account.pack_id = packInfo.id;
            }

            return account;
        }catch(e){
            console.error(e)
        }
    }

    async loginZhimaAccount(account){
        try{
            if(!this.conn) this.conn = await getSesameConnect();
            const wt_user_package = new table(this.conn, "wt_user_package");
            //登录账户
            console.log("登录账户");
            let header = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36'};
            let indexReq = await request.get('http://web.http.cnapi.cc/core/api/verify').set(header);
            let jsonCallback = `jQuery${SESAME.__self__randomString()}_${new Date().getTime()}`;
            header.cookie = SESAME.__self__generateCookiesString(indexReq.headers["set-cookie"]);
            header.Referer = 'http://h.zhimaruanjian.com/';
            let loginReq = await request.get(`http://web.http.cnapi.cc/index/users/login_do?jsonpcallback=${jsonCallback}&phone=${account.tel}&password=${account.password}&remember=false&_=${new Date().getTime()}`).set(header);
            let reg = new RegExp(`^${jsonCallback}\\("(.*)"\\)$`);
            let callbackBody = loginReq.text.match(reg) ? JSON.parse(loginReq.text.match(reg)[1].replace(/\\"/g,"\"")) : {};
            console.log(callbackBody.msg);
            if(callbackBody.code !== "1") throw new Error("账户登录错误:" + JSON.stringify(callbackBody));
            header.cookie = SESAME.__self__generateCookiesString(loginReq.headers["set-cookie"]);
            header.Referer = 'http://h.zhimaruanjian.com/ucenter/';

            console.log("获取今日免费额度");
            let freePackJsonCallback = `jQuery${SESAME.__self__randomString()}_${new Date().getTime()}`;
            let freeReg = new RegExp(`^${freePackJsonCallback}\\("(.*)"\\)$`);
            console.log(SESAME.__self__generateCookiesString(loginReq.headers["set-cookie"]));
            let freePack = await request.get(`http://web.http.cnapi.cc/index/users/get_day_free_pack?jsonpcallback=${freePackJsonCallback}&_=${new Date().getTime()}`).set(header);
            let freePackBody = freePack.text.match(freeReg) ? JSON.parse(freePack.text.match(freeReg)[1].replace(/\\"/g,"\"")) : {};
            console.log("获取今日免费额度", freePackBody.msg);
            if(freePackBody.code !=="1") throw new Error("获取免费额度错误:" + JSON.stringify(freePackBody));
            return await wt_user_package.where({uid:account.user_id, package_type:7}).find().one();
        }catch(e){
            console.error(e);
            throw e;
        }
    }
    /**
     * 获取package内ip
     * @param account
     * @returns {Promise<void>}
     */
    async getSesameIPList (account) {
        try{
            if(!this.conn) this.conn = await getSesameConnect();
            const wt_wirte_list = new table(this.conn, "wt_wirte_list");
            let whiteIpInfo = await wt_wirte_list.where({mark_ip:this.whiteIP}).find().one();
            if(!whiteIpInfo){
                await wt_wirte_list.insert({
                    uid:account.user_id
                    , mark_ip:this.whiteIP
                    , ip:SESAME.__self__ip_2_decm(this.whiteIP)
                    , created_at: parseInt(new Date().getTime()/1000)
                    , updated_at: parseInt(new Date().getTime()/1000)
                }).run();
            } else {
                await wt_wirte_list.where({mark_ip:this.whiteIP}).update({uid:account.user_id}).run();
            }

            let ipApi = `http://webapi.http.zhimacangku.com/getip?num=200&type=2&pro=0&city=0&yys=0&port=1&pack=20718&ts=1&ys=1&cs=1&lb=1&sb=0&pb=45&mr=2&regions=`;
            let proxyIp = new mongo(this.db, 'backend.proxy.ip');
            await proxyIp.remove({expire_time:{$lt:new Date()}});
            let list = await request.get(ipApi);
            let body = JSON.parse(list.text);
            if(body.code === 0 ){
                let listAll = body.data;
                for(let ip of listAll){
                    await proxyIp.update({ip:ip.ip, port:ip.port}, {$set:{expire_time:new Date(ip.expire_time), isSesame:true, tel:account.tel, uid:account.user_id
                            , useTime: new Date(new Date().getTime() - 35 * 1000)}})
                }
            } else {
                switch(body.code){
                    default:
                        throw Error(body.msg);
                }
            }
        }catch(e){
            console.error(e.message);
            if(e.response){
                const fs = require("fs");
                fs.writeFileSync(__dirname + "/errpage.html", e.response.text)
            }
        }
    }

    /**
     * 删除无法连接的IP
     * @param ip
     * @param port
     * @returns {Promise<void>}
     */
    async removeIP(ip, port){
        try {
            console.log("remove", ip, port);
            const proxyIpCache = new mongo(this.db, 'backend.proxy.ip.cache');
            const proxyIp = new mongo(this.db, 'backend.proxy.ip');
            await proxyIpCache.remove({ip, port});
            await proxyIp.remove({ip, port});
        } catch (e) {
            console.error(e);
        }
    }

    async fixedRecord(proxyInfo){
        try{
            if(!this.conn) this.conn = await getSesameConnect();
            const wt_use_record_package = new table(this.conn, "wt_use_record_package");
            const wt_user_package = new table(this.conn, "wt_user_package");
            await SESAME.__self__sleep(5);
            let recordInfo = await wt_use_record_package.where({uid:proxyInfo.uid, mark_need_ip:proxyInfo.ip}).order('created_at', -1).find().one();
            if(recordInfo){
                console.log(proxyInfo.uid, recordInfo.package_balance, recordInfo.id);
                let lastRecordInfo = await wt_use_record_package.where({id:{$lt:recordInfo.id}}).order('created_at', -1).find().one();
                delete lastRecordInfo.id;
                delete lastRecordInfo.need_ip;
                delete lastRecordInfo.created_at;
                delete lastRecordInfo.updated_at;
                delete lastRecordInfo.price;
                delete lastRecordInfo.mark_need_ip;
                await wt_use_record_package.where({id:recordInfo.id}).update(lastRecordInfo).run();
                await wt_user_package.where({uid:proxyInfo.uid, package_type:1}).update({
                    package_balance:200
                }).run();
            }
        }catch(e){
            throw e;
        }
    }
}

module.exports = SESAME;